﻿#region BarChartCylindarAxis UI Control
//
// All rights are reserved. Reproduction or transmission in whole or in part, in 
// any form or by any means, electronic, mechanical or otherwise, is prohibited 
// without the prior written consent of the copyright owner.
// 
// Filename: BarChartCylindarAxis.xaml.cs
//
#endregion 
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DiscoveryLogic.DataServices.DataSources;
using DiscoveryLogic.UI.Layouts.Controls;
using DiscoveryLogic.UI.Layouts.Converters;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    /// <summary>
    /// Container class for displaying the stacked cylindars representing components 
    /// of Mortgage payment.  This has been built to specifically display this particular model.
    /// </summary>
    public partial class BarChartCylindarAxis : UserControl, IGraphControl
    {
        
        // Variables for Mouse Events

        /// <summary>
        /// flag indicating mouse over control
        /// </summary>
        private bool isMouseOver = false;

        /// <summary>
        /// flag indicating mouse right button depressed
        /// </summary>
        private bool isMouseDown = false;

        /// <summary>
        /// initial X position of mouse when move event fired
        /// </summary>
        private double originMouseX;

        /// <summary>
        /// initial Y position of mouse when move event fired
        /// </summary>
        private double originMouseY;

        /// <summary>
        /// end X position of mouse when move event fired
        /// </summary>
        private double currentMouseX;

        /// <summary>
        /// end Y position of mouse when move event fired
        /// </summary>
        private double currentMouseY;

        /// <summary>
        /// Change in 100ths of an inch that the slider has moved
        /// </summary>
        private double delta;

        /// <summary>
        /// holder for Slider canvas node
        /// </summary>
        private Canvas slider = null;

        /// <summary>
        /// arbitrary maximum payment
        /// </summary>
        private double maxPayment = 5000;

        /// <summary>
        /// holder for minimum payment, which gets set based on the model.
        /// </summary>
        private double minPayment = 0;

        /// <summary>
        /// Holder of Account info
        /// </summary>
        private Account model;

        /// <summary>
        /// getter/setter for model
        /// </summary>
        public Account Model
        {
            get { return model; }
            set { 
                model = value;
                InitializeSlider();
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public BarChartCylindarAxis()
        {
            InitializeComponent();
            TextBlock minPayment = MinimumPayment;
           
            BarChartCylindars barCylindars = new BarChartCylindars();
            barCylindars.SetValue(Canvas.NameProperty, "Bars");
            barCylindars.SetValue(Canvas.LeftProperty, 20.0);
            barCylindars.SetValue(Canvas.TopProperty, 110.0);
            barCylindars.Width = 77.8;
            barCylindars.Height = 155.441;
            CylindarCanvas.Children.Add(barCylindars);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="model">Account object representing mortgage account</param>
        public BarChartCylindarAxis(Account model)
        {
            this.model = model;
            InitializeComponent();
            TextBlock minPayment = MinimumPayment;
            minPayment.Text = String.Format((string)Resources["Res_MinPayment"], model.MinimumPayment.ToCurrency());

            BarChartCylindars barCylindars = new BarChartCylindars();
            barCylindars.SetValue(Canvas.NameProperty, "Bars");
            barCylindars.SetValue(Canvas.LeftProperty, 20.0);
            barCylindars.SetValue(Canvas.TopProperty, 110.0);
            barCylindars.Width = 77.8;
            barCylindars.Height = 155.441;
            Root.Children.Add(barCylindars);
        }

        /// <summary>
        /// event handler for MouseEnterEvent;
        /// </summary>
        /// <param name="sender">Slider canvas</param>
        /// <param name="args">event args</param>
        protected void CanvasMouseEnter(object sender, EventArgs args)
        {
            

            Canvas slider = (Canvas)sender;
            slider.Cursor = Cursors.Hand; 
        }


        /// <summary>
        /// event handler for MouseLeave
        /// </summary>
        /// <param name="sender">Slider canvas</param>
        /// <param name="args"></param>
        protected void CanvasMouseLeave(object sender, EventArgs args)
        {
            

            Canvas slider = (Canvas)sender;
            slider.Cursor = Cursors.Arrow ; 
        }


        /// <summary>
        /// Event handler for MouseLeftButtonDown
        /// </summary>
        /// <param name="sender">Slider Canvas</param>
        /// <param name="args">event args</param>
        public void CanvasMouseLeftButtonDown(object sender, MouseEventArgs args)
        {
            isMouseDown = true;

            originMouseX = ((MouseEventArgs)args).GetPosition(null).X;

            originMouseY = ((MouseEventArgs)args).GetPosition(null).Y;

            SliderThumb.CaptureMouse();
        }

        /// <summary>
        /// Event handler for MouseLeftButtonUp
        /// </summary>
        /// <param name="sender">Slider Canvas</param>
        /// <param name="args">event args</param>
        public void CanvasMouseLeftButtonUp(object sender, EventArgs args)
        {            
            SliderThumb.ReleaseMouseCapture();
            isMouseDown = false;
        }

        /// <summary>
        /// Event handler for MouseMove
        /// </summary>
        /// <param name="sender">Slider Canvas</param>
        /// <param name="args">event args</param>
        public void CanvasMouseMove(object sender, MouseEventArgs args)
        {

            // If mouse button is still in down position
            if (isMouseDown)
            {
                // Retrieve current mouse position
                currentMouseX = args.GetPosition(null).X;
                currentMouseY = args.GetPosition(null).Y;

                double deltaY = (currentMouseY - originMouseY);

                // Set slider thumb position
                SetSlider(deltaY);

                // Raise Slider change method
                OnSliderChanged(new EventArgs());
            }            
            
        }

        /// <summary>
        /// Sets position of cylindars and slider based on model
        /// </summary>
        private void InitializeSlider()
        {
            if (slider == null)
            {
                slider = VerticalSlider;
            }

            double sliderTop = (double)slider.GetValue(Canvas.TopProperty);
            Path axis = Vertical;
            minPayment = ((MortgageAccount)model).MinimumPayment;//((MortgageAccount)model).PaymentSchedule.StartDate.AddMonths(((MortgageAccount)model).PaymentSchedule.TotalPayments);
            double principal = ((MortgageAccount)model).NextPaymentPrincipal;
            double interest = ((MortgageAccount)model).NextPaymentInterest;
            double nextPayment = ((MortgageAccount)model).NextPaymentAmount;
            Interest.Text = string.Format((string)Resources["Res_Interest"], interest.ToCurrency());
            MinimumPayment.Text = string.Format((string)Resources["Res_MinPayment"], minPayment.ToCurrency());
            double initialOffset = (double)axis.GetValue(Canvas.TopProperty) + axis.Height;
            double minPaymentPos = ValueToPosition(minPayment, axis.Height, maxPayment);
            double principalPos = ValueToPosition(nextPayment, axis.Height, maxPayment);
            double interestPos = ValueToPosition(interest, axis.Height, maxPayment);
            double paymentPos = principalPos;
            
            // Set Min Payment marker

            MinimumPaymentLine.SetValue(Canvas.TopProperty, initialOffset - minPaymentPos);
            MinimumPayment.SetValue(Canvas.TopProperty, (initialOffset - minPaymentPos) - (MinimumPayment.Height / 1.5));
            
            // Set Interest marker

            InterestLine.SetValue(Canvas.TopProperty, initialOffset - interestPos);
            Interest.SetValue(Canvas.TopProperty, (initialOffset - interestPos) - (Interest.Height / 2));

            // Set slider min and max market
            double value = 0;
            SliderMin.Text = value.ToCurrency();
            SliderMin.SetValue(Canvas.TopProperty, initialOffset - (SliderMin.Height/2));
            
            SliderMax.Text = maxPayment.ToCurrency();
            SliderMax.SetValue(Canvas.TopProperty, (double)axis.GetValue(Canvas.TopProperty) - (SliderMax.Height/2));
            SliderMaxLine.SetValue(Canvas.TopProperty, axis.GetValue(Canvas.TopProperty));
            


            // Update slider text
            double sliderThumbHeight = SliderThumb.Height / 2;
            double sliderBarMax = (double)axis.GetValue(Canvas.TopProperty)
                                - sliderThumbHeight / 2;
            double sliderBarMin = (double)axis.GetValue(Canvas.TopProperty)
                                + axis.Height
                                - sliderThumbHeight / 2;

            double pathLength = axis.Height;
            double sliderPosition = paymentPos;
            double sliderAmount = maxPayment * (sliderPosition / pathLength);

            MonthlyPayment.Text = Math.Round(sliderAmount).ToCurrency();



            // Hide blocking text
            HideBlockingLabels(initialOffset - principalPos);

            SetSliderAbsolute(paymentPos + (SliderThumb.Height / 2), principalPos - 5, interestPos, minPaymentPos);
        }

        /// <summary>
        /// Converts a currency value to a position along the axis
        /// </summary>
        /// <param name="valueToPlot">The currency value from the model</param>
        /// <param name="axisLength">Length of the slider axis</param>
        /// <param name="maxValue">The currency value represented by the top of the slider axis</param>
        /// <returns></returns>
        private double ValueToPosition(double valueToPlot, double axisLength, double maxValue)
        {
            return (valueToPlot / maxValue) * axisLength;
        }

        /// <summary>
        /// Converts a position along the axis to a currency value
        /// </summary>
        /// <param name="position">the position along the axis</param>
        /// <param name="axisLength">total length of the axis in 1/100''</param>
        /// <param name="maxValue">Maximum currency value represented by the top of the slider axis</param>
        /// <returns></returns>
        private double PositionToValue(double position, double axisLength, double maxValue)
        {
            return (position / axisLength) * maxValue;
        }


        /// <summary>
        /// Set the slider and cylindars to a specific position
        /// </summary>
        /// <param name="pos">position to set the slider</param>
        /// <param name="principalPos">position to set the top of the principal cylindar</param>
        /// <param name="interestPos">position to set the top of the interest cylindar</param>
        /// <param name="minPaymentPos">position to set the minimum payment line</param>
        public void SetSliderAbsolute(double pos, double principalPos , double interestPos, double minPaymentPos)
        {
            Path axis = Vertical;

            double initialOffset = (double)axis.GetValue(Canvas.TopProperty)
                                    + axis.Height;
            bool paymentLevelValid = false;
            if (slider == null)
            {
                slider = VerticalSlider;
            }

            
            Controls.BarChartCylindars cylindars = (Controls.BarChartCylindars)Root.FindName("Bars");
            double currentCanvasTop = Convert.ToDouble(slider.GetValue(Canvas.TopProperty));
            if (cylindars != null)
            {
                paymentLevelValid = cylindars.SetCylindar(principalPos, interestPos, minPaymentPos);
            }

            // Reset the location of the object
            if (paymentLevelValid)
            {
                slider.SetValue(
                    Canvas.TopProperty,
                    initialOffset - pos 
                    );

                // Update the beginning position of the mouse
                originMouseX = currentMouseX;
                originMouseY = currentMouseY;

            }

        }

        /// <summary>
        /// Hides min payment and/or interest label if they're blocking the way
        /// </summary>
        /// <param name="labelPosition">position of the payment label</param>
        private void HideBlockingLabels(double labelPosition)
        {
            // Hide min payment label if it is in the way            
            double minPaymentTextTop = (double)MinimumPayment.GetValue(Canvas.TopProperty);

            if (Math.Abs(labelPosition - minPaymentTextTop) < MinimumPayment.Height
                || Math.Abs(minPaymentTextTop - labelPosition) < MinimumPayment.Height)
            {
                MinimumPayment.Opacity = 0;
            }
            else
            {
                MinimumPayment.Opacity = 1;
            }

            // Hide interest label if it is in the way
            double InterestTextTop = (double)Interest.GetValue(Canvas.TopProperty);

            if (Math.Abs(labelPosition - InterestTextTop) < Interest.Height
                || Math.Abs(InterestTextTop - labelPosition) < Interest.Height)
            {
                Interest.Opacity = 0;
            }
            else
            {
                Interest.Opacity = 1;
            }

            // Hide max label if it is in the way
            TextBlock MaxText = SliderMax;
            double MaxTextTop = (double)MaxText.GetValue(Canvas.TopProperty);

            if (Math.Abs(labelPosition - MaxTextTop) < MaxText.Height
                || Math.Abs(MaxTextTop - labelPosition) < MaxText.Height)
            {
                MaxText.Opacity = 0;
            }
            else
            {
                MaxText.Opacity = 1;
            }

        }


        #region IGraphControl Members

        /// <summary>
        /// Sets the position of the slider based in a change (delta) along the slider axis
        /// </summary>
        /// <param name="delta">change in slider position of 1/100"</param>
        public void SetSlider(double delta)
        {

            if (slider == null)
            {
                slider = VerticalSlider;
            }

            // Find slider thumb and its dimensions
            double sliderThumbHeight = (double)SliderThumb.GetValue(Ellipse.HeightProperty);

            // Find slider bar and its dimensions
            Path sliderBar = Vertical;
            double sliderBarMax = (double)sliderBar.GetValue(Canvas.TopProperty)
                                - sliderThumbHeight / 2;
            double sliderBarMin = (double)sliderBar.GetValue(Canvas.TopProperty)
                                + sliderBar.Height
                                - sliderThumbHeight / 2;

            double sliderMinAllowable = (double)MinimumPaymentLine.GetValue(Canvas.TopProperty) 
                                        - (sliderThumbHeight / 2);

            double newValue = (double)slider.GetValue(Canvas.TopProperty) 
                               + delta;

            // Ensure that slider thumb doesn`t go beyond min or max
            if (newValue < sliderBarMax)
            {
                newValue = sliderBarMax;
                delta = sliderBarMax
                        - (double)slider.GetValue(Canvas.TopProperty);
            }

            if (newValue > sliderMinAllowable)
            {
                newValue = sliderMinAllowable;
                delta = sliderMinAllowable 
                        - (double)slider.GetValue(Canvas.TopProperty);
            }


            this.delta = delta;

            // Set bars on bar chart
            Controls.BarChartCylindars cylindars = (Controls.BarChartCylindars)Root.FindName("Bars");
            double currentCanvasTop = Convert.ToDouble(slider.GetValue(Canvas.TopProperty));
            bool paymentLevelValid = false;
            if (cylindars != null)
            {
                paymentLevelValid = cylindars.AlterCylindar(delta);
            }

            // Reset the location of the object
            if (paymentLevelValid)
            {
                slider.SetValue(Canvas.TopProperty, newValue);

                // Update slider text
                
                // Calculate and update amount textblock beside slider thumb
                TextBlock paymentText = MonthlyPayment;
                double pathLength = sliderBarMin - sliderBarMax;
                double sliderPosition = newValue  - sliderBarMax;
                double sliderAmount = maxPayment * (1 - (sliderPosition / pathLength));
                double currentAmount = Math.Round(sliderAmount);
                
                paymentText.Text = Math.Round(sliderAmount).ToCurrency();

                // Hide blocking text
                HideBlockingLabels(currentCanvasTop + delta);                

                // Update the beginning position of the mouse
                originMouseX = currentMouseX;
                originMouseY = currentMouseY;
            }

        }


        #endregion

        #region IGraphControl Members
        
            /// <summary>
            /// Returns the last change applied.
            /// </summary>
            /// <returns></returns>
            public double GetLastSliderChange() {
                return delta;
            }

            /// <summary>
            /// event raised when slider position changed.
            /// </summary>
            public event SliderChangedEventHandler SliderChanged;


            /// <summary>
            /// Called to call the SliderChanged callback.
            /// </summary>
            /// <param name="e"></param>
            public void OnSliderChanged(EventArgs e)
            {
                if (SliderChanged != null)
                {
                    SliderChanged(this, e);
                }
            }

            /// <summary>
            /// Gets distance from slider thumb to min of slider bar
            /// </summary>
            /// <returns></returns>
            public double GetCurrentPosition()
            {
                double sliderThumbHeight = (double)SliderThumb.GetValue(Ellipse.HeightProperty);
                Canvas slider = VerticalSlider;

                double sliderMinAllowable = (double)MinimumPaymentLine.GetValue(Canvas.TopProperty)
                                            - (sliderThumbHeight / 2);

                return Math.Abs((double)slider.GetValue(Canvas.TopProperty) + (slider.Height / 2)
                        - sliderMinAllowable);
            }

            public double GetSliderLength()
            {
                // Find slider thumb and its dimensions
                double sliderThumbHeight = (double)SliderThumb.GetValue(Ellipse.HeightProperty);

                // Find slider bar and its dimensions
                Path sliderBar = Vertical;
                double sliderBarMax = (double)sliderBar.GetValue(Canvas.TopProperty)
                                    - sliderThumbHeight / 2;
                double sliderBarMin = (double)sliderBar.GetValue(Canvas.TopProperty)
                                    + sliderBar.Height
                                    - sliderThumbHeight / 2;

                double sliderMinAllowable = (double)MinimumPaymentLine.GetValue(Canvas.TopProperty)
                                            - (sliderThumbHeight / 2);

                return (sliderMinAllowable - sliderBarMax);
            }

            /// <summary>
            /// Set slider position based on a percentage
            /// </summary>
            /// <param name="percent"></param>
            public void SetSliderPercent(double percent)
            {
                double sliderThumbHeight = (double)SliderThumb.GetValue(Ellipse.HeightProperty);

                Path sliderBar = Vertical;

                Canvas slider = VerticalSlider;

                double sliderBarMax = (double)sliderBar.GetValue(Canvas.TopProperty)
                                    - sliderThumbHeight / 2;

                double offset = percent * this.GetSliderLength();
                double desiredPosition = sliderBarMax + offset;

                this.SetSlider(desiredPosition
                               - ((double)slider.GetValue(Canvas.TopProperty) + sliderThumbHeight / 2));
            }

        #endregion

    }
}
